﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
using WinRTToolkit.IO;

namespace WinRTToolkit.Storage
{
    public class IsolatedStorageHelper
    {
        #region Members

        private StorageFolder _rootFolder;

        #endregion

        #region Constructor

        public IsolatedStorageHelper(IsolatedStorageScope scope)
        {
            switch (scope)
            {
                case IsolatedStorageScope.LocalFolder:
                    _rootFolder = ApplicationData.Current.LocalFolder;
                    break;
                case IsolatedStorageScope.RoamingFolder:
                    _rootFolder = ApplicationData.Current.RoamingFolder;
                    break;
                case IsolatedStorageScope.TemporaryFolder:
                    _rootFolder = ApplicationData.Current.TemporaryFolder;
                    break;
                default:
                    // TODO Exception
                    throw new Exception();
            }
        }

        #endregion

        #region Public Methods

        #region Folder

        /// <summary>
        /// [Async] Get Folder by path
        /// </summary>
        /// <param name="path">Path of the folder</param>
        /// <returns>Task of StorageFolder</returns>
        public async Task<StorageFolder> GetFolder(string path)
        {
            return await GetFolderAsync(path);
        }

        public async Task<bool> FolderExists(string path)
        {
            var folder = await GetFolder(path);

            return folder != null;
        }

        public async Task<IEnumerable<StorageFolder>> GetRootFolders()
        {
            return await GetFolders(_rootFolder);
        }

        public async Task<IEnumerable<StorageFolder>> GetFolders(string path)
        {
            var folder = await GetFolder(path);
            return await GetFolders(folder);
        }

        public async Task<IEnumerable<StorageFolder>> GetFolders(StorageFolder folder)
        {
            return await GetFoldersAsync(folder);
        }

        public async Task<StorageFolder> CreateFolder(string path)
        {
            return await CreateFolderAsync(path);
        }

        public async Task<bool> DeleteFolder(string path)
        {
            var folder = await GetFolder(path);

            if (folder != null)
                return await DeleteFolder(folder);

            return false;
        }

        public async Task<bool> DeleteFolder(StorageFolder folder)
        {
            return await DeleteFolderAsync(folder);
        }

        #endregion

        #region File

        public async Task<StorageFile> GetFile(string path)
        {
            return await GetFileAsync(path);
        }

        public async Task<StorageFile> GetFile(string path, FileMode fileMode)
        {
            var file = await GetFile(path);

            if (fileMode == FileMode.Create || (file == null && fileMode == FileMode.OpenOrCreate))
                file = await CreateFile(path);

            return file;
        }

        public async Task<bool> FileExists(string path)
        {
            var file = await GetFile(path);
            return file != null;
        }

        public async Task<IEnumerable<StorageFile>> GetFiles(string path)
        {
            var folder = await GetFolder(path);
            return await GetFiles(folder);
        }

        public async Task<IEnumerable<StorageFile>> GetFiles(StorageFolder folder)
        {
            return await GetFilesAsync(folder);
        }

        public async Task<StorageFile> CreateFile(string path, bool overrideExisting = false)
        {
            return await CreateFileAsync(path, overrideExisting == true ?
                CreationCollisionOption.ReplaceExisting : CreationCollisionOption.OpenIfExists);
        }

        public async Task<IRandomAccessStream> OpenRead(string path)
        {
            var file = await GetFile(path);
            return await OpenRead(file);
        }

        public async Task<IRandomAccessStream> OpenRead(StorageFile file)
        {
            if (file == null)
                return null;

            return await OpenAsync(file, FileAccessMode.Read);
        }

        public async Task<IRandomAccessStream> OpenWrite(string path, FileMode fileMode)
        {
            var file = default(StorageFile);

            if (fileMode == FileMode.Create)
                file = await CreateFile(path, true);
            else
            {
                file = await GetFile(path);

                if (file == null && fileMode == FileMode.OpenOrCreate)
                    file = await CreateFile(path, true);
            }

            return await OpenWrite(file);
        }

        public async Task<IRandomAccessStream> OpenWrite(StorageFile file, OpenMode openMode)
        {
            if (file != null && openMode == OpenMode.Override)
            {
                var path = file.Path.Replace(_rootFolder.Path, "");
                file = await CreateFile(path, true);
            }

            var stream = await OpenWrite(file);

            if (stream != null && openMode == OpenMode.Append)
                stream.Seek(stream.Size);

            return stream;
        }

        public async Task<bool> DeleteFile(string path)
        {
            var file = await GetFile(path);

            if (file != null)
                return await DeleteFile(file);

            return false;
        }

        public async Task<bool> DeleteFile(StorageFile file)
        {
            return await DeleteFileAsync(file);
        }

        public async Task<T> LoadObject<T>(string path)
        {
            var file = await GetFile(path);
            return await LoadObject<T>(file);
        }

        public async Task<T> LoadObject<T>(StorageFile file)
        {
            T result = default(T);

            // Verification de l'existence du fichier
            if (file == null)
                return result;

            using (var access = await OpenRead(file))
            {
                if (access != null)
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
                    result = (T)xmlSerializer.Deserialize(access.AsStreamForRead());
                }
            }

            // Retour du résultat
            return result;
        }

        public async Task<bool> SaveObject<T>(T item, string path)
        {
            bool result = false;

            using (var access = await OpenWrite(path, FileMode.Create))
            {
                if (access != null)
                {
                    using (var writer = new StreamWriter(access.AsStreamForWrite()))
                    {
                        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

                        // Ecriture du fichier sérialisé
                        xmlSerializer.Serialize(writer, item);
                        result = true;
                    }
                }
            }

            return result;
        }

        public async Task<bool> SaveStream(string path, Stream stream)
        {
            var result = false;

            using (var access = await OpenWrite(path, FileMode.Create))
            {
                if (access != null)
                {
                    using (var dw = new DataWriter(access))
                    {
                        using (var sr = new StreamReader(stream))
                        {
                            var data = await sr.ReadToEndAsync();
                            dw.WriteString(data);
                            await dw.StoreAsync();
                            await dw.FlushAsync();
                            result = true;
                        }
                    }
                }
            }

            return result;
        }

        public async Task<DateTimeOffset> GetDateModified(StorageFile file)
        {
            var properties = await GetBasicProperties(file);

            if (properties == null)
                return DateTimeOffset.MinValue;

            return properties.DateModified;
        }

        public async Task<BasicProperties> GetBasicProperties(StorageFile file)
        {
            if (file == null)
                return null;

            return await GetBasicPropertiesAsync(file);
        }

        #endregion

        #endregion

        #region Private Methods

        private async Task<StorageFolder> GetFolderAsync(string path)
        {
            try
            {
                return await _rootFolder.GetFolderAsync(path);
            }
            catch { }

            return null;
        }

        private async Task<IEnumerable<StorageFolder>> GetFoldersAsync(StorageFolder folder)
        {
            try
            {
                return await folder.GetFoldersAsync();
            }
            catch { }

            return null;
        }

        private async Task<StorageFolder> CreateFolderAsync(string path)
        {
            try
            {
                return await _rootFolder.CreateFolderAsync(path);
            }
            catch { }

            return null;
        }

        private async Task<bool> DeleteFolderAsync(StorageFolder folder)
        {
            try
            {
                await folder.DeleteAsync(StorageDeleteOption.PermanentDelete);
                return true;
            }
            catch { }

            return false;
        }

        public async Task<StorageFile> GetFileAsync(string path)
        {
            try
            {
                return await _rootFolder.GetFileAsync(path);
            }
            catch { }

            return null;
        }

        private async Task<IEnumerable<StorageFile>> GetFilesAsync(StorageFolder folder)
        {
            try
            {
                return await folder.GetFilesAsync();
            }
            catch { }

            return null;
        }

        private async Task<StorageFile> CreateFileAsync(string path, CreationCollisionOption creationCollisionOption = CreationCollisionOption.OpenIfExists)
        {
            try
            {
                return await _rootFolder.CreateFileAsync(path, creationCollisionOption);
            }
            catch { }

            return null;
        }

        private async Task<bool> DeleteFileAsync(StorageFile file)
        {
            try
            {
                await file.DeleteAsync(StorageDeleteOption.PermanentDelete);
                return true;
            }
            catch { }

            return false;
        }

        private async Task<IRandomAccessStream> OpenWrite(StorageFile file)
        {
            if (file == null)
                return null;

            return await OpenAsync(file, FileAccessMode.ReadWrite);
        }

        private async Task<IRandomAccessStream> OpenAsync(StorageFile file, FileAccessMode mode)
        {
            try
            {
                return await file.OpenAsync(mode);
            }
            catch { }

            return null;
        }

        private async Task<BasicProperties> GetBasicPropertiesAsync(StorageFile file)
        {
            try
            {
                return await file.GetBasicPropertiesAsync();
            }
            catch { }

            return null;
        }

        #endregion
    }
}
